#include "TextureManager.h"									

TextureManager TextureManager::m_Instance;

//Textures////////////////////////////////////////////////////////////////
TextureManager::TextureInfo::TextureInfo()
{
	this->ID = BAD_INDEX;
	strcpy_s(this->filename, 8,"INVAILD");
}

void TextureManager::TextureInfo::Create(const char* file)
{
	strcpy_s(this->filename, strlen(file)+1, file);
	glGenTextures(1, &this->ID);					// Generate OpenGL texture IDs
	glBindTexture(GL_TEXTURE_2D, this->ID);			// Bind Our Texture
}

void TextureManager::TextureInfo::Delete()
{
	glDeleteTextures(1, &this->ID);
}

//Texture Manager/////////////////////////////////////////////////////////
TextureManager::TextureManager()
{
	TextureList.clear();
}

TextureManager::~TextureManager()
{
	Shutdown();
}

GLbyte *TextureManager::gltLoadTGA(const char *szFileName, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
{
	FILE *pFile;			// File pointer
	TGAHEADER tgaHeader;		// TGA file header
	unsigned long lImageSize;		// Size in bytes of image
	short sDepth;			// Pixel depth;
	GLbyte	*pBits = NULL;          // Pointer to bits

	// Default/Failed values
	*iWidth = 0;
	*iHeight = 0;
	*eFormat = GL_BGR_EXT;
	*iComponents = GL_RGB8;

	// Attempt to open the file
	fopen_s(&pFile, szFileName, "rb");
	if(pFile == NULL)
		return NULL;

	// Read in header (binary)
	fread(&tgaHeader, 18/* sizeof(TGAHEADER)*/, 1, pFile);

	// Get width, height, and depth of texture
	*iWidth = tgaHeader.width;
	*iHeight = tgaHeader.height;
	sDepth = tgaHeader.bits / 8;

	// Put some validity checks here. Very simply, I only understand
	// or care about 8, 24, or 32 bit targa's.
	if(tgaHeader.bits != 8 && tgaHeader.bits != 24 && tgaHeader.bits != 32)
		return NULL;

	// Calculate size of image buffer
	lImageSize = tgaHeader.width * tgaHeader.height * sDepth;

	// Allocate memory and check for success
	pBits = (GLbyte*)malloc(lImageSize * sizeof(GLbyte));
	if(pBits == NULL)
		return NULL;

	// Read in the bits
	// Check for read error. This should catch RLE or other 
	// weird formats that I don't want to recognize
	if(fread(pBits, lImageSize, 1, pFile) != 1)
	{
		free(pBits);
		return NULL;
	}

	// Set OpenGL format expected
	switch(sDepth)
	{
	case 3:     // Most likely case
		*eFormat = GL_BGR_EXT;
		*iComponents = GL_RGB8;
		break;
	case 4:
		*eFormat = GL_BGRA_EXT;
		*iComponents = GL_RGBA8;
		break;
	case 1:
		*eFormat = GL_LUMINANCE;
		*iComponents = GL_LUMINANCE8;
		break;
	};

	// Done with File
	fclose(pFile);

	// Return pointer to image data
	return pBits;
}

TextureManager::TextureInfo TextureManager::LoadTGA(const char* fileName, TextureParam Param)
{
	TextureInfo texture;
	GLbyte* pBits;
	int iComp;
	GLenum iFormat;


	//Bitmaps are 4 bytes aligned, if unknown use 1 to be safe
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	//Reading Pixel data from file and saving it
	pBits = gltLoadTGA(fileName, &texture.width, &texture.height, &iComp, &iFormat);

	if (!pBits)
	{
		char err[128];
		sprintf_s(err, 128, "Error Loading TGA file: %s", fileName);
		MessageBoxA(0, err, "OpenGL Texture Manager", MB_OK); 
		return texture;
	}

	// This also BINDS the texture
	texture.Create(fileName);

	// Give OpenGL the data to make a texture
	//glTexImage2D(GL_TEXTURE_2D, 0, iComp, texture.width, texture.height, 0, iFormat, GL_UNSIGNED_BYTE, pBits);
	//MipMap!
	gluBuild2DMipmaps(GL_TEXTURE_2D, iComp, texture.width, texture.height, iFormat, GL_UNSIGNED_BYTE, pBits);
	delete[] pBits;

	//Set up texture params
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, Param);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, Param);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	// Note: Keith - from - (JB) you must unbind your textures afterwards so things don't get screwy
	glBindTexture(GL_TEXTURE_2D, -1);

	return texture;
}

int TextureManager::LoadTexture(const char* fileName, TextureParam param)
{
	if(fileName == NULL) return -1;

	for (unsigned int i = 0; i < TextureList.size(); ++i)
	{
		if (strcmp(TextureList[i].filename, fileName) == 0)
		{
			return TextureList[i].ID;
		}
	}

	TextureInfo newTex = LoadTGA(fileName, param);

	TextureList.push_back(newTex);

	return TextureList.back().ID;
}

void TextureManager::UnloadTexture(GLuint ID)
{
	if (!TextureList.size())
	{
		return;
	}

	std::vector<TextureInfo>::iterator TexIter;
	TexIter = TextureList.begin();

	while (TexIter != TextureList.end())
	{
		if ((*TexIter).ID == ID)
		{
			(*TexIter).Delete();
			TextureList.erase(TexIter);
		}
		else
		{
			TexIter++;
		}
	}
}

int TextureManager::GetTextureID(const char* fileName)
{
	if (!TextureList.size())
	{
		return BAD_INDEX;
	}

	std::vector<TextureInfo>::iterator TexIter;
	TexIter = TextureList.begin();

	while (TexIter != TextureList.end())
	{
		if (strcmp((*TexIter).filename, fileName) == 0)
		{
			return (*TexIter).ID;
		}
		else
		{
			TexIter++;
		}
	}

	//NOT FOUND
	return BAD_INDEX;
}

void TextureManager::Shutdown()
{
	while (!TextureList.empty())
	{
		TextureList.back().Delete();
		TextureList.pop_back();
	}
}